﻿import mx.utils.Delegate;

dynamic class KeyListener {
	//--------------------------------------------------------------------------------------------------  static message
	public static var M_ONPRESSCASE:String = "onPressCase";
	//--------------------------------------------------------------------------------------------------  private
	private var $rootmc:MovieClip;
	private var $enabled:Boolean = true;
	private var $keyObject:Object;
	private var $keyStates:Object;
	private var $PressCase:Object;
	private var $checkPressEventOnly:Boolean = true;
	private var $lastActionKey:Number;
	private var $defaultEndTimeSp:Number = 500;
	private var $defaultPressTimeSp:Number = 200;
	//--------------------------------------------------------------------------------------------------  public
	public function get CheckPressOnly():Boolean {
		return this.$checkPressEventOnly;
	}
	public function set CheckPressOnly(value:Boolean) {
		this.$checkPressEventOnly = value;
	}
	public function get enabled():Boolean {
		return this.$enabled;
	}
	public function set enabled(value:Boolean) {
		this.$enabled = value;
	}
	
	//--------------------------------------------------------------------------------------------------  function
	public function KeyListener(_mc:MovieClip) {
		initsay();
		if (_mc == undefined) {
			_mc = _root;
		}
		$rootmc = _mc;
		initvars();
		initKeyTracking();
		AsBroadcaster.initialize(this);
		_say.msay("inited!");
	}
	
	private function initvars():Void{
		$keyStates = new Object();
		this.$enabled = true;
		$PressCase = new Object();
	}
	
	private function initKeyTracking():Void{
		if (this.$keyObject != undefined) {
			Key.removeListener(this.$keyObject);
		}
		this.$keyObject = new Object();
		this.$keyObject.onKeyDown = Delegate.create(this, this.onKeyDownFun);
		this.$keyObject.onKeyUp = Delegate.create(this, this.onKeyUpFun);
		Key.addListener(this.$keyObject);
	}
	
	private function onKeyDownFun() {
		if (!this.$enabled) return;
		var kcode:Number=Key.getCode()
		_say.msay(kcode);
		var oldSt:Boolean = this.$keyStates[kcode.toString()].state;
		this.$keyStates[kcode.toString()] = getStateObj(true);
		this.$lastActionKey = kcode;
		if(oldSt!=true){
			this.checkPressCases();
		}
	}
	private function onKeyUpFun() {
		if (!this.$enabled) return;
		var kcode:Number=Key.getCode()
		//_say.msay(kcode);
		this.$keyStates[kcode.toString()] = getStateObj(false);
		this.$lastActionKey = kcode;
		if(!this.$checkPressEventOnly){
			this.checkPressCases();
		}
	}
	
	
	private function checkPressCases() {
		for (var i in this.$PressCase) {
			checkPressCase(i,this.$PressCase[i]);
		}
	}
	private function checkPressCase(caseName:String,caseobj:Object) {
		var keys:Array = caseobj.keys;
		var timesp:Array = caseobj.timesp;
		var endtimesp:Number = caseobj.endtimesp;
		var state:Number = caseobj.state;
		var lasttime:Number = caseobj.lasttime;
		
		var keytime:Number = (new Date()).getTime();
		var OK:Boolean = true;
		
		var timetemp:Number=keytime - lasttime
		//trace("--------"+caseName+"["+state+"] ->"+Key.getCode()+" ---------");
		if ((timetemp < endtimesp) && state == -1) {
			trace(" timetemp < endtimesp("+timetemp+"<"+endtimesp+")")
			return;
		}else {
			if(state>-1){
				if ((keytime - lasttime) > timesp[state]) {
					state = -1;
					caseobj.state = -1;
				}
			}
			
		//trace("  ======= "+ state);
			var nowKeys:Object = keys[state + 1];
			if (typeof(nowKeys) == "object" || nowKeys.length>0) {
				for (var i = 0; i < nowKeys.length; i++) {
					var keystate:Object = this.$keyStates[nowKeys[i]];
					if (!keystate.state) {
						OK = false;
						break;
					}else {
						keytime = Math.max(keytime,keystate.time);
					}
				}
				
			}else {
				var keystate:Object = this.$keyStates[nowKeys.toString()];
				trace("CR: "+nowKeys+" === "+keystate.state)
				if (!keystate.state) {
					OK = false;
				}else {
					keytime = Math.max(keytime,keystate.time);
				}
			}
		}
		

		//trace("  2" + OK);
		// 判断是否按键正确
			if (OK) {
				caseobj.state += 1;
				caseobj.lasttime = keytime;
			}else {
				//caseobj.lasttime = keytime;
				caseobj.state = -1;
				return;
			}
			
		//trace("  3" + OK);
		//判断是否是最后一个键
		//Debug.trace(caseobj);
			if (caseobj.state >= keys.length - 1) {
				var a = caseobj.keys[caseobj.keys.length-1].toString();
				if (a[0] != undefined) {
					var b=a.join(",");
					if (b.indexOf(this.$lastActionKey.toString()) < 0) {
						return;
					}
				}else {
					if (a != this.$lastActionKey.toString()) {
						return;
					}
				}
				this.broadcastMessage(M_ONPRESSCASE, caseName);
				if (this[M_ONPRESSCASE] != undefined) {
					this[M_ONPRESSCASE](caseName);
				}
				_say.msay("Do: "+caseName);
				caseobj.lasttime = keytime;
				caseobj.state = -1;
				return;
			}
	}
	/**
	 * 重置所有按键状态
	 */
	public function ResetState() {
		this.$keyStates = new Object();
	}
	/**
	 * 增加press case 到监听中。
	 * @param	caseName 唯一的press case号
	 * @param	keys 按键序列
	 * @param	timesp 各按键时间间隔。
	 * @param	endtimesp 结束时间判断。(在该时间内铵键无效)
	 */
	public function AddPressCase(caseName:String,keys:Array, timesp:Array, endtimesp:Number) {
		if (keys.length <= 0 || keys == undefined) {
			_say.esay("AddPressCase error , no keys is empty");
			return;
		}
		if (this.$PressCase[caseName] != undefined) {
			_say.esay("AddPressCase error ,CaseName["+caseName+"] is exist!");
			return;
		}
		keys = fixKeys(keys);
		if (timesp == undefined || timesp == null || timesp == undefined) {
			var i = keys.length;
			timesp = new Array();
			while (i-- > 1) {
				timesp.push($defaultPressTimeSp);
			}
		}
		
		if (endtimesp == undefined || endtimesp<0) {
			endtimesp = $defaultEndTimeSp;
		}
		this.$PressCase[caseName] = { keys:keys, timesp:timesp, endtimesp:endtimesp, state:-1 ,lasttime:0};
	}
	/**
	 * 删除press case
	 * @param	caseName press case 号
	 * @return
	 */
	public function DelPressCase(caseName:String):Object {
		var obj = this.$PressCase[caseName];
		delete this.$PressCase[caseName];
		return obj;
	}
	private function fixKeys(keys:Array):Array {
		for (var i = 0; i < keys.length; i++) {
			var type:String = typeof(keys[i]);
			if (type != "string" && keys[i].length>0 && type!="number") {
				keys[i] = fixKeys(keys[i]);
			}else if(type == "string"){
				keys[i] = keys[i].toUpperCase().charCodeAt(0);
			}else if (type == "number") {
				keys[i] = keys[i];
			}
		}
		return keys;
	}
	
	private function getStateObj(state:Boolean):Object {
		return {state:state,time:(new Date()).getTime()};
	}
	//--------------------------------------------------------------------------------------------------  say
	private var _say:Say;
	private function initsay() {
		_say = new Say("KeyListener");
		this[Say.M_SAY] = this.sayfun;
		_say.addListener(this);
	}
	private function sayfun(o) {
		trace(o)
	}

}